import static org.junit.Assert.*;

import org.junit.Test;

import junit.framework.TestCase;
/**
 * @author Viji
 *
 */
public class VendingMachineTest extends TestCase {
	
	
	VendingMachine VM1;
	VendingMachineItem VMItem1, VMItem2, VMItem3, VMItem4;
		
	protected void setUp() throws Exception {
		VM1 = new VendingMachine();
		VMItem1= new VendingMachineItem("VMItem1", 20.0);
		VM1.addItem(VMItem1, "B");
	}

	/** 
	 * Purpose: 		To test the addItem() method 
	 * Functionality: 	If item was added properly to the vending machine
	 * Input: 			Item added in setUp() method
	 * Output: 			getItem() method should return the item added.
	 */
	@Test  
	public void testAddItem() {
		assertEquals(VMItem1, VM1.getItem("B"));
	}
	
	/** 
	 * Purpose: 		To test the addItem() method 
	 * Functionality:	If exception occurs to add in non empty slot
	 * Input: 			addItem() to the slot B already occupied 
	 * Output: 			Exception 
	 */
	@Test
	public void testAddItem1() {
		try{
			VMItem2= new VendingMachineItem("VMItem2", 50.0);
			VM1.addItem(VMItem2, "B");
			fail("Should have thrown an VendingMachineException because slot is not empty");
		} 
		catch (VendingMachineException e) {
			assertEquals( "Slot B already occupied",e.getMessage());
		}
	}
	
	/** 
	 * Purpose: 		To test the addItem() method 
	 * Functionality:	If exception occurs if invalid code is given
	 * Input: 			addItem() to invalid slot 
	 * Output: 			Exception 
	 */
	@Test
	public void testAddItem2() {
		try{
			VM1.addItem(VMItem2, "E_CODE");
			fail("Should have thrown an VendingMachineException because code is invalid");
		} 
		catch (VendingMachineException e) {
			assertEquals( "Invalid code for vending machine item",e.getMessage());
		}
	}
	
/** 
	 * Purpose: 		To test the addItem() method 
	 * Functionality:	If exception occurs if null code is given -Failed
	 * Input: 			addItem() to null slot 
	 * Output: 			Exception 
	 
	@Test
	public void testAddItem3() {
		try{
			VM1.addItem(VMItem1, null);
			//assertEquals(VMItem1, VM1.getItem("A"));
			fail("Should have thrown an VendingMachineException because code is null");
		}
		catch (VendingMachineException e) {
			assertEquals( "Code value cannot be null",e.getMessage());
		}
	*/
	
	

/** 
 * Purpose: 		To test the addItem() method 
 * Functionality: 	If undefined item was added to the vending machine
 * Input: 			vending machine item declared, but not defined
 * Output: 			should not add the item.
 
@Test  
public void testAddItem4() {
	//VendingMachineItem pepsi;
	VM1.addItem(VMItem3, "A");
	assertEquals(VMItem3, VM1.getItem("A"));
	VM1.insertMoney(5);
	VM1.makePurchase("A");
	assertEquals(0.0, VM1.getBalance(), 0.01);
	
}
*/	
	/** 
	 * Purpose: 		To test the removeItem() method 
	 * Functionality:	If valid item is removed from the valid slot
	 * Input: 			removeItem()  
	 * Output: 			Item removed from vending machine slot 
	 */
	@Test
	public void testRemoveItem(){
		assertEquals(VMItem1,VM1.removeItem("B"));
		assertNull(VM1.getItem("B"));
	}
	
	/** 
	 * Purpose: 		To test the removeItem() method 
	 * Functionality:	If exception occurs if trying to remove an item from empty slot
	 * Input: 			removeItem() from slot which does not have an item(empty) 
	 * Output: 			Exception 
	 */
	@Test
	public void testRemovetem1() {
		try{
				VM1.removeItem("C");
				fail("Should have thrown an VendingMachineException because slot is empty");
		} 
		catch (VendingMachineException e) {
				assertEquals( "Slot C is empty -- cannot remove item",e.getMessage());
		}
	}

	/** 
	 * Purpose: 		To test the insertMoney() method 
	 * Functionality:	If exception occurs if trying to insert negative money
	 * Input: 			negative amount to insertMoney() method 
	 * Output: 			Exception 
	 */
	@Test
	public void testInsertMoney() {
		try{
			VM1.insertMoney(-20.0);
			fail("Should have thrown an VendingMachineException because amount is negative");
		} 
		catch (VendingMachineException e) {
			assertEquals( "Invalid amount.  Amount must be >= 0",e.getMessage());
		}
	}
	
	/** 
	 * Purpose: 		To test the insertMoney() with getBalance() method 
	 * Functionality:	If check the balance calculation functionality
	 * Input: 			Insert money twice with amount greater than and equal to 0
	 * Output: 			Balance sum of money inserted
	 */
	@Test
	public void testGetBalance(){
		VM1.insertMoney(20.0);
		assertEquals(20.0, VM1.getBalance(), 0.01);
		VM1.insertMoney(30.0);
		assertEquals(50.0, VM1.getBalance(), 0.01);
		VM1.insertMoney(0.0);
		assertEquals(50.0, VM1.getBalance(), 0.01);
	}

	/** 
	 * Purpose: 		To test the makePurchase() method 
	 * Functionality:	If the methods fails when money inserted is less than sale price of item
	 * Input: 			insertMoney 10.0 
	 * Output: 			Purchase Fails
	 */
	@Test
	public void testMakePurchase1(){
		VM1.insertMoney(10.0);
		assertFalse(VM1.makePurchase("B"));
	}
	
	/** 
	 * Purpose: 		To test the makePurchase() method 
	 * Functionality:	To check if purchase fails when purchasing an invalid code
	 * Input: 			makePurchase a C slot which had no item 
	 * Output: 			Purchase fails 
	 */
	@Test
	public void testMakePurchase2(){
		VM1.insertMoney(20.0);
		assertFalse(VM1.makePurchase("C"));
	}
	
	/** 
	 * Purpose: 		To test the makePurchase() method 
	 * Functionality:	To check if purchase succeeds when having enough money and valid code for the item
	 * Input: 			makePurchase with 20.0 inserted for 20.0 item 
	 * Output: 			purchase succeeds with balance updated 
	 */
	
	@Test
	public void testMakePurchase(){
		VM1.insertMoney(20.0);
		assertTrue(VM1.makePurchase("B"));
		assertEquals(0.0, VM1.getBalance(), 0.01);
	}
	
	/** 
	 * Purpose: 		To test the returnChange() method 
	 * Functionality:	To check if correct balance is returned after purchase, and if balance is reset.
	 * Input: 			insert 30.0, purchase 20.0
	 * Output: 			returnChange 10.0 first time, returnChange 0.0 after that
	 */
	@Test
	public void testReturnChange(){
		VM1.insertMoney(30.0);
		VM1.makePurchase("B");
		assertEquals(10.0, VM1.returnChange(), 0.01);
		assertEquals(0.0, VM1.returnChange(), 0.01);
	}
	
	/**
	 * Purpose		: Combine many tests together
	 * Functionality: To check if methods work when called together
	 */
	@Test
	public void testAll(){
		VendingMachineItem pepsi= new VendingMachineItem("Pepsi", 1.25);
		VM1.addItem(pepsi, "A");
		VendingMachineItem chips= new VendingMachineItem("Chips", 0.99);
		VM1.addItem(chips, "C");
		VMItem4= new VendingMachineItem("VMItem4", 20.0);
		VM1.addItem(VMItem4, "D");
		VM1.insertMoney(28.50);
		assertTrue(VM1.makePurchase("B"));
		assertEquals(8.50, VM1.getBalance(), 0.01);
		assertFalse(VM1.makePurchase("B"));
		assertEquals(8.50, VM1.getBalance(), 0.01);
		assertTrue(VM1.makePurchase("C"));
		assertEquals(7.51, VM1.getBalance(), 0.01);
		assertTrue(VM1.makePurchase("A"));
		assertEquals(6.26, VM1.getBalance(), 0.01);
		VM1.insertMoney(10.0);
		VM1.addItem(chips, "C");
		assertTrue(VM1.makePurchase("C"));
		assertEquals(15.27, VM1.getBalance(), 0.01);
		assertFalse(VM1.makePurchase("D"));
		assertEquals(15.27, VM1.returnChange(), 0.01);
		assertEquals(0.0, VM1.returnChange(), 0.01);
	}
	
}
